/**
 * 本地录音 
 * 该js 支持本地录音输出MP3 同时支持通过websocket即时传录音音频流
 * @param {*录音配置对象} config {                
 *   支持录音动态效果canvas，config添加配置属性 
 *   {
 *       canvasWidth: 100, //画布宽度
 *       canvasHeight: 50, //画布高度
 *       canvasCtx: canvasCtx, //画布内容，
 *       funOk: ()=>{}, // 初始化成功回调
 *       funCancel: ()=>{}, // 初始化成功回调
 *       funAudioprocess: ()=>{}, // processor.onaudioprocess持续不断的返回采样数据后的处理方法, 暴露一个方法处理采样数据
 *       若需要通过websocket发送流给后端传入此方法
 *   }}
 * 
 * MP3Recorder 使用
 * var recorder = new MP3Recorder(config)
 * 
 * API：
 * recorder.start()  开始录音
 * recorder.stop()  结束录音
 * recorder.draw() 绘制音柱canvas图
 * recorder.getMp3Blob() 获取录音blob
 * 
 *  注意：
 *      js中引入了worker-realtime.js及lame.min.js，需要对应引入才能生效。
 *      须把worker-realtime.js及lame.min.js放置到public/static/js/recorder中。
 *   
 *      由于js是单线程的,为保持性能,recordmp3.js通过new Worker(URL)创建了一个专用Web worker(另起一个线程),
 *      它只执行URL指定的脚本,使用Blob URL作为参数。URL必须为程序同源目录下的js文件路径
 * 
 *  浏览器录音权限：localhost/https  
 *  如果为http想调用, 浏览器前往chrome://flags/#unsafely-treat-insecure-origin-as-secure 添加信任的地址,多个地址逗号隔开,并设置为enabled。
 */

function MP3Recorder(config) {
    var recorder = this;
    config = config || {};
    config.sampleRate = config.sampleRate || 44100;
    config.bitRate = config.bitRate || 128;

    navigator.getUserMedia = navigator.getUserMedia ||
        navigator.webkitGetUserMedia ||
        navigator.mozGetUserMedia ||
        navigator.msGetUserMedia;

    if (navigator.getUserMedia) {
        navigator.getUserMedia({
                audio: true
            },
            function (stream) {
                var context = new AudioContext(),
                    microphone = context.createMediaStreamSource(stream),
                    processor = context.createScriptProcessor(16384, 1, 1), //bufferSize大小，输入channel数，输出channel数
                    mp3ReceiveSuccess, currentErrorCallback;

                var height = config.canvasHeight || 100;
                var width = config.canvasWidth || 400;

                const analyser = context.createAnalyser()
                analyser.fftSize = 1024
                //连接到音频源
                microphone.connect(analyser);
                analyser.connect(context.destination);

                const bufferLength = analyser.frequencyBinCount // 返回的是 analyser的fftsize的一半
                const dataArray = new Uint8Array(bufferLength);
                
                recorder.draw = function () {
                    const canvasCtx = config.canvasCtx;
                    if (!canvasCtx) return;
                    canvasCtx.clearRect(0, 0, width, height); //清除画布
                    analyser.getByteFrequencyData(dataArray); // 将当前频率数据复制到传入其中的Uint8Array
                    // const requestAnimFrame = window.requestAnimationFrame(draw) || window.webkitRequestAnimationFrame(draw);
                    canvasCtx.fillStyle = '#000130';
                    canvasCtx.fillRect(0, 0, width, height);
                    let barWidth = (width / bufferLength) * 2;
                    let barHeight;
                    let x = 0;
                    let c = 2
                    for (let i = 0; i < bufferLength; i++) {
                        barHeight = c + (dataArray[i] / 400) * height;
                        canvasCtx.fillStyle = 'rgb(0, 255, 30)';
                        canvasCtx.fillRect(x, height / 2 - barHeight / 2, barWidth, barHeight);
                        x += barWidth + 1;
                    }
                }

                config.sampleRate = context.sampleRate;
                processor.onaudioprocess = function (event) {
                    //边录音边转换
                    var array = event.inputBuffer.getChannelData(0);
                    realTimeWorker.postMessage({
                        cmd: 'encode',
                        buf: array
                    });
                    typeof config.funAudioprocess === 'function' && config.funAudioprocess();
                };
                // url必须在public下
                var realTimeWorker = new Worker('./static/js/recorder/worker-realtime.js');
                realTimeWorker.onmessage = function (e) {
                    switch (e.data.cmd) {
                        case 'init':
                            log('初始化成功');
                            if (config.funOk) {
                                config.funOk();
                            }
                            break;
                        case 'end':
                            log('MP3大小：', e.data.buf.length);
                            if (mp3ReceiveSuccess) {
                                mp3ReceiveSuccess(new Blob(e.data.buf, {
                                    type: 'audio/mp3'
                                }));
                            }
                            break;
                        case 'error':
                            log('错误信息：' + e.data.error);
                            if (currentErrorCallback) {
                                currentErrorCallback(e.data.error);
                            }
                            break;
                        default:
                            log('未知信息：', e.data);
                    }
                };

                recorder.getMp3Blob = function (onSuccess, onError) {
                    currentErrorCallback = onError;
                    mp3ReceiveSuccess = onSuccess;
                    realTimeWorker.postMessage({
                        cmd: 'finish'
                    });
                };

                recorder.start = function () {
                    if (processor && microphone) {
                        microphone.connect(processor);
                        processor.connect(context.destination);
                        log('开始录音');
                    }
                }

                recorder.stop = function () {
                    if (processor && microphone) {
                        realTimeWorker.postMessage({
                            cmd: 'end',
                            buf: [],
                            action: '1'
                        });
                        microphone.disconnect();
                        processor.disconnect();
                        log('录音结束');
                    }
                }

                realTimeWorker.postMessage({
                    cmd: 'init',
                    config: {
                        sampleRate: config.sampleRate,
                        bitRate: config.bitRate
                    }
                });
            },
            function (error) {
                var msg;
                switch (error.code || error.name) {
                    case 'PERMISSION_DENIED':
                    case 'PermissionDeniedError':
                        msg = '用户拒绝访问麦客风';
                        break;
                    case 'NOT_SUPPORTED_ERROR':
                    case 'NotSupportedError':
                        msg = '浏览器不支持麦客风';
                        break;
                    case 'MANDATORY_UNSATISFIED_ERROR':
                    case 'MandatoryUnsatisfiedError':
                        msg = '找不到麦客风设备';
                        break;
                    default:
                        msg = '无法打开麦克风，异常信息:' + (error.code || error.name);
                        break;
                }
                if (config.funCancel) {
                    config.funCancel(msg);
                }
            });
    } else {
        if (config.funCancel) {
            config.funCancel('当前浏览器不支持录音功能');
        }
    }

    function log(str) {
        if (config.debug) {
            console.log(str);
        }
    }
}

export default MP3Recorder;